home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / netmail / rnr214.zip / EXEC23P.ZIP / SPAWN.ASM < prev    next >
Assembly Source File  |  1990-10-11  |  21KB  |  1,114 lines

  1. ;
  2. ;    --- Version 2.3 90-10-11 21:26 ---
  3. ;
  4. ;    SPAWN.ASM - Main function for memory swapping spawn call.
  5. ;
  6. ;    Public Domain Software written by
  7. ;        Thomas Wagner
  8. ;        Ferrari electronic GmbH
  9. ;        Beusselstrasse 27
  10. ;        D-1000 Berlin 21
  11. ;        Germany
  12. ;
  13. ;    PASCAL:
  14. ;        function do_spawn (method: byte; 
  15. ;                   swapfname, execfname, cmdtail: string; 
  16. ;                   envlen: word; var envp)
  17. ;    C:
  18. ;        int do_spawn (unsigned char method, 
  19. ;                  char *swapfname, char *execfname, char *cmdtail,
  20. ;                      unsigned envlen, char *envp)
  21. ;
  22. ;    Assemble with
  23. ;
  24. ;    tasm  /DPASCAL spawn          - Turbo Pascal (Tasm only), near
  25. ;    tasm  /DPASCAL /DFARCALL spawn    - Turbo Pascal (Tasm only), far
  26. ;    ?asm  spawn;              - C, default model (small)
  27. ;    ?asm  /DMODL=large spawn      - C, large model
  28. ;    
  29. ;    NOTE:    For C, change the 'model' directive below according to your
  30. ;        memory model, or define MODL=xxx on the command line.
  31. ;
  32. ;    The 'method' parameter determines the swap/spawn/exec
  33. ;    function:
  34. ;        00 = Spawn, don't swap
  35. ;        01 = Spawn, swap
  36. ;        80 = Exec, don't swap
  37. ;
  38. ;    If swapping (01), the following flags can be ORed into 'method':
  39. ;
  40. ;        02 = Use EMS if possible
  41. ;        04 = Use 'create temporary file' call (swapfname is path only)
  42. ;
  43. ;
  44. ;    For 'cmdtail' and 'execfname', the first byte must contain 
  45. ;    the length of the string, even when calling from C.
  46. ;
  47. ;    'swapfname' and 'execfname' must be zero terminated, even when
  48. ;    calling from Pascal.
  49. ;
  50.     IFDEF    PASCAL
  51.     .model    tpascal
  52. ;
  53. ptrsize    =    1
  54.     ELSE
  55.     IFNDEF    MODL
  56.     .model    small,c
  57.     ELSE
  58. %    .model    MODL,c
  59.     ENDIF
  60. ;
  61. ptrsize    =    @DataSize
  62.     ENDIF
  63. ;
  64. stacklen    =    160        ; 80 word local stack
  65. ;
  66. blocksize    =    16 * 1024    ; Write block size
  67. blocksize_paras    =    blocksize / 16
  68. blockshift    =    10        ; shift factor for paragraphs
  69. blockmask    =    blocksize_paras - 1    ; Write block mask
  70. ;
  71. ; Method flags
  72. ;
  73. SWAPPING    =    01h
  74. USE_EMS        =    02h
  75. CREAT_TEMP    =    04h
  76. TERMINATE    =    80h
  77. ;
  78. EMM_INT        =    67h
  79. ;
  80. exec_block    struc
  81. envseg    dw    ?
  82. ppar    dw    ?
  83. pparseg    dw    ?
  84. fcb1    dw    ?
  85. fcb1seg    dw    ?
  86. fcb2    dw    ?
  87. fcb2seg    dw    ?
  88. exec_block    ends
  89. ;
  90. mcb        struc
  91. id        db    ?
  92. owner        dw    ?
  93. paras        dw    ?
  94.         db    3 dup(?)
  95. mcb_reserved    db    ?
  96. mcb        ends
  97. ;
  98. next_mcbs    struc
  99. next_addr    dw    ?
  100. next_size    dw    ?
  101. next_blocks    dw    ?
  102. next_rest    dw    ?
  103. next_mcbs    ends
  104. ;
  105. ;----------------------------------------------------------------------
  106. ;
  107. ;    The memory structure for the shrunk-down code
  108. ;
  109. parseg    struc
  110. parbeg    db    5ch dup(?)        ; start after PSP
  111. ;
  112. page_frame    dw    ?        ; EMS page frame (0 if no EMS)
  113. memsize        dw    ?        ; total paragraphs
  114. rd_blocks    dw    ?        ; number of blocks in swapfile
  115. rd_rest        dw    ?        ; number of bytes in last block
  116. save_ss        dw    ?        ; saved global ss
  117. save_sp        dw    ?        ; saved global sp
  118. spx        dw    ?        ; saved local sp
  119. next_mcb    db    TYPE next_mcbs dup(?)
  120. expar        db    TYPE exec_block dup (?) ; exec-parameter-block
  121. zero        dw    ?        ; Zero command tail length (dummy)
  122. method        db    ?        ; method parameter
  123. handle        dw    ?        ; swap file or EMS handle (0 for EXEC)
  124. tempfilename    db    81 dup(?)    ; swap file name
  125. ;
  126. parseg    ends
  127. ;
  128.     .data
  129.     IFDEF    PASCAL
  130.     extrn    prefixseg: word
  131.     ELSE
  132.     extrn    _psp: word
  133.     ENDIF
  134. ;
  135. ;
  136.     .code
  137. ;
  138. ;       This part of the program code will be moved to address
  139. ;    'codeloc' and executed there.
  140. ;
  141. ;    Registers on entry:
  142. ;        BX    = paragraphs to keep
  143. ;        CX     = length of environment to copy or zero
  144. ;        DS:SI    = environment source
  145. ;        ES:DI    = environment destination
  146. ;        (ES = our low core code segment)
  147. ;
  148. doexec:
  149.     jcxz    noenvcpy
  150.     rep movsb
  151. noenvcpy:
  152.     push    es            ; DS = ES = low core
  153.     pop    ds
  154.     cmp    ds:handle,0
  155.     je    no_shrink
  156.         mov    ah,04ah
  157.     int     21h                     ; free memory
  158.     mov    bx,ds:next_mcb.next_addr
  159. ;
  160. free_loop:
  161.     or    bx,bx
  162.     jz    no_shrink
  163.     mov    es,bx
  164.     mov    bx,es:mcb_reserved.next_addr
  165.     mov    ax,es
  166.     inc    ax
  167.     mov    es,ax
  168.     mov    ah,049h
  169.     int    21h
  170.     jmp    free_loop
  171. ;
  172. no_shrink:
  173.     push    ds
  174.     pop    es
  175.     mov    dx,filename        ; params for exec
  176.     mov    bx,expar
  177.     mov    ax,04b00h
  178.     int    21h            ; exec
  179. ;
  180.     mov    bx,cs
  181.     mov    ds,bx
  182.     mov    es,bx
  183.     mov    ss,bx
  184.     mov    sp,ds:spx
  185.     cld
  186.     push    ax
  187.     pushf
  188. ;
  189.     test    ds:method,TERMINATE    ; exec?
  190.     jz    exec_ckswap
  191.     jmp    exec_term        ; terminate if yes
  192. ;
  193. exec_ckswap:
  194.     test    ds:method,SWAPPING    ; swap?
  195.     jnz    exec_noterm        ; go swap back in if yes
  196.     jmp    exec_retn        ; return if spawn and no swap
  197. ;
  198. exec_noterm:
  199.     mov    ah,4ah            ; expand memory
  200.     mov    bx,ds:memsize
  201.     int    21h
  202.     jnc    exec_memok
  203.     jmp    exec_term        ; terminate if error
  204. ;
  205. ;    Swap memory back
  206. ;
  207. exec_memok:
  208.     cmp    ds:page_frame,0
  209.     jne    exec_swems
  210.     jmp    exec_swfile        ; go swap from file if not EMS
  211. ;
  212. ;    Swap in memory from EMS
  213. ;
  214. exec_swems:
  215.     mov    ax,cs
  216.     dec    ax
  217.     push    ax            ; push current MCB
  218.     push    ds            ; push data segment
  219.     mov    ax,next_mcb
  220.     push    ax            ; and next MCB pointer
  221.     mov    dx,ds:handle
  222.     mov    cx,ds:rd_blocks
  223.     push    ds:rd_rest        ; push bytes in last block
  224.     mov    ds,ds:page_frame
  225.     xor    bx,bx
  226.     mov    di,swapbeg
  227. ;
  228. swap_in_ems:
  229. ;
  230.     jcxz    swin_ems_rest
  231. ;
  232. swin_ems:
  233.     push    cx
  234.     mov    ax,4400h
  235.     int    EMM_INT
  236.     or    ah,ah
  237.     jnz    exec_term
  238.     mov    cx,blocksize
  239.     mov    si,0
  240.     push    di
  241.     rep movsb
  242.     pop    di
  243.     mov    ax,es
  244.     add    ax,blocksize_paras
  245.     mov    es,ax
  246.     pop    cx
  247.     inc    bx
  248.     loop    swin_ems
  249. ;
  250. swin_ems_rest:
  251.     pop    cx
  252.     jcxz    swin_ems_rdy
  253.     mov    ax,4400h
  254.     int    EMM_INT
  255.     or    ah,ah
  256.     jnz    exec_term
  257.     mov    si,0
  258.     rep movsb
  259.     inc    bx
  260. ;
  261. swin_ems_rdy:
  262.     pop    si            ; next offset
  263.     pop    ds            ; segment
  264.     pop    es            ; current MCB
  265.     mov    ax,[si].next_addr
  266.     or    ax,ax
  267.     jz    swin_ems_complete
  268.     push    ax            ; next MCB
  269.     push    ax            ; also is data segment for next struc
  270.     mov    cx,mcb_reserved
  271.     push    cx            ; next offset
  272.     mov    ax,[si].next_rest
  273.     push    ax            ; bytes in last block
  274. ;
  275.     push    bx
  276.     push    dx
  277.     call    get_mcb            ; alloc MCB, ES = dest segment
  278.     pop    dx
  279.     pop    bx
  280.     mov    cx,[si].next_blocks    ; number of blocks
  281.     mov    ds,cs:page_frame    ; reload page frame addr
  282.     mov    di,0
  283.     jmp    swap_in_ems
  284. ;
  285. swin_ems_complete:
  286.     mov    ah,45h
  287.     int    EMM_INT
  288.     mov    ax,cs
  289.     mov    es,ax
  290.     mov    ds,ax
  291.     jmp    exec_retn
  292. ;
  293. exec_term:
  294.     cmp    cs:page_frame,0
  295.     je    eterm_noems
  296.     mov    dx,cs:handle
  297.     mov    ah,45h
  298.     int    EMM_INT
  299. eterm_noems:
  300.     mov    ax,4c00h                ; terminate process
  301.         int     21h
  302. ;
  303. ;    Swap in memory from file
  304. ;
  305. exec_swfile:
  306.     mov    ax,cs
  307.     dec    ax
  308.     push    ax            ; push current MCB
  309.     push    ds            ; push data segment
  310.     mov    ax,next_mcb
  311.     push    ax            ; and next MCB pointer
  312.     push    ds:rd_rest        ; push bytes in last block
  313. ;
  314.     mov    dx,tempfilename
  315.     mov    ax,3d92h        ; open exclusive, R/W
  316.     int    21h
  317.     jc    exec_term        ; abort if file not found
  318.     mov    ds:handle,ax
  319.     mov    bx,ax
  320. ;
  321.     mov    cx,blocksize
  322.     mov    dx,swapbeg
  323.     mov    si,ds:rd_blocks
  324. ;
  325. exec_rdblocks:
  326.     mov    ah,3fh            ; read file
  327.     cmp    si,0
  328.     je    exec_rdr
  329.     dec    si
  330.     int    21h
  331.     jc    exec_term        ; terminate if error reading
  332.     mov    ax,ds
  333.     add    ax,blocksize_paras
  334.     mov    ds,ax
  335.     jmp    exec_rdblocks
  336. ;
  337. exec_rdr:
  338.     pop    cx            ; bytes in last block
  339.     jcxz    exec_norest
  340.     int    21h
  341.     jc    exec_term        ; terminate if error reading
  342. ;
  343. exec_norest:
  344.     pop    si            ; next offset
  345.     pop    ds            ; segment
  346.     pop    es            ; current MCB
  347.     mov    ax,[si].next_addr
  348.     or    ax,ax
  349.     jz    swin_file_complete
  350.     push    ax            ; next MCB
  351.     push    ax            ; also is data segment for next struc
  352.     mov    cx,mcb_reserved
  353.     push    cx            ; next offset
  354.     mov    ax,[si].next_rest
  355.     push    ax            ; bytes in last block
  356. ;
  357.     push    bx
  358.     call    get_mcb            ; alloc MCB, ES = dest segment
  359.     pop    bx
  360.     mov    si,[si].next_blocks    ; number of blocks
  361.     mov    ax,es
  362.     mov    ds,ax
  363.     mov    cx,blocksize
  364.     mov    dx,0
  365.     jmp    exec_rdblocks
  366. ;
  367. swin_file_complete:
  368.     mov    ah,3eh
  369.     int    21h            ; close file
  370.     mov    ax,cs
  371.     mov    ds,ax
  372.     mov    es,ax
  373. ;
  374. exec_retn:
  375.     popf
  376.     pop    ax
  377.     jc    exec_fault        ; return EXEC error code if fault
  378.     mov    ah,4dh            ; else get program return code
  379.     int    21h
  380. ;    ret    far            ; can't use a RET here since
  381.     db    0cbh            ; we are using .model
  382. ;
  383. exec_fault:
  384.     mov    ah,3            ; return error as 03xx
  385. ;    ret    far
  386.     db    0cbh
  387. ;
  388. ;
  389. ;    get_mcb allocates a block of memory by modifying the MCB chain
  390. ;    directly.
  391. ;
  392. ;    On entry, DS:SI points to the next_mcbs descriptor for the block,
  393. ;          ES:0  is the MCB for the current block.
  394. ;
  395. ;    On exit,  ES is the wanted MCB.
  396. ;
  397. ;    Uses     AX, BX, CX
  398. ;
  399. get_mcb    proc    near
  400. ;     
  401.     cmp    es:id,4dh        ; 4d means normal MCB
  402.     jnz    gmcb_abort        ; halt if no next
  403.     mov    ax,es            ; current MCB
  404.     add    ax,es:paras
  405.     inc    ax
  406.     mov    es,ax            ; next MCB
  407.     cmp    ax,[si].next_addr
  408.     ja    gmcb_abort        ; halt if next MCB > wanted
  409.     je    mcb_found        ; jump if same addr as wanted
  410.     add    ax,es:paras        ; last addr
  411.     cmp    ax,[si].next_addr
  412.     jb    get_mcb            ; loop if last < wanted
  413.     cmp    es:owner,0
  414.     jne    gmcb_abort        ; halt if not free
  415. ;
  416. ;    The wanted MCB starts within the current MCB. We now have to
  417. ;    create a new MCB at the wanted position, which is initially
  418. ;    free, and shorten the current MCB to reflect the reduced size.
  419. ;
  420.     mov    bx,es            ; current
  421.     inc    bx            ; + 1 (header doesn't count)
  422.     mov    ax,[si].next_addr
  423.     sub    ax,bx            ; paragraphs between MCB and wanted
  424.     mov    bx,es:paras        ; paras in current MCB
  425.     sub    bx,ax            ; remaining paras
  426.     dec    bx            ; -1 for header
  427.     mov    es:paras,ax        ; set new size for current
  428.     mov    cl,es:id        ; old id
  429.     mov    es:id,4dh        ; set id: there is a next
  430.     mov    ax,[si].next_addr    ; now point to new MCB
  431.     mov    es,ax
  432.     mov    es:id,cl        ; and init to free
  433.     mov    es:owner,0
  434.     mov    es:paras,bx
  435. ;
  436. ;    We have found an MCB at the right address. If it's not free,
  437. ;    abort. Else check the size. If the size is ok, we're done 
  438. ;    (more or less).
  439. ;
  440. mcb_found:
  441.     mov    es,ax
  442.     cmp    es:owner,0
  443.     je    mcb_check        ; continue if free
  444. ;
  445. gmcb_abort:
  446.     jmp    exec_term
  447. ;
  448. mcb_check:
  449.     mov    ax,es:paras        ; size
  450.     cmp    ax,[si].next_size    ; size needed
  451.     jae    mcb_ok            ; ok if enough space
  452. ;
  453. ;    If there's not enough room in this MCB, check if the next
  454. ;    MCB is free, too. If so, coalesce both MCB's and check again.
  455. ;
  456.     cmp    es:id,4dh
  457.     jnz    gmcb_abort        ; halt if no next
  458.     push    es            ; save current
  459.     mov    bx,es
  460.     add    ax,bx
  461.     inc    ax            ; next MCB
  462.     mov    es,ax
  463.     cmp    es:owner,0        ; next free ?
  464.     jne    gmcb_abort        ; halt if not
  465.     mov    ax,es:paras        ; else load size
  466.     inc    ax            ; + 1 for header
  467.     mov    cl,es:id        ; and load ID
  468.     pop    es            ; back to last MCB
  469.     add    es:paras,ax        ; increase size
  470.     mov    es:id,cl        ; and store ID
  471.     jmp    mcb_check        ; now try again
  472. ;
  473. ;    The MCB is free and large enough. If it's larger than the
  474. ;    wanted size, create another MCB after the wanted.
  475. ;
  476. mcb_ok:
  477.     mov    bx,es:paras
  478.     sub    bx,[si].next_size
  479.     jz    mcb_no_next        ; ok, no next to create
  480.     push    es
  481.     dec    bx            ; size of next block
  482.     mov    ax,es
  483.     add    ax,[si].next_size
  484.     inc    ax            ; next MCB addr
  485.     mov    cl,es:id        ; id of this block
  486.     mov    es,ax            ; address next
  487.     mov    es:id,cl        ; store id
  488.     mov    es:paras,bx        ; store size
  489.     mov    es:owner,0        ; and mark as free
  490.     pop    es            ; back to old MCB
  491.     mov    es:id,4dh        ; mark next block present
  492.     mov    ax,[si].next_size    ; and set size to wanted
  493.     mov    es:paras,ax
  494. ;
  495. mcb_no_next:
  496.     mov    es:owner,cx        ; set owner to current PSP
  497.     ret                ; all finished (whew!)
  498. ;
  499. get_mcb    endp
  500. ;
  501. ireti:
  502.     iret
  503. ;
  504. iretoff    =    offset ireti - offset doexec
  505. ;
  506. execlen    =    $-doexec
  507. ;
  508. ;--------------------------------------------------------------------
  509. ;
  510. parseg2    struc
  511.         db    TYPE parseg dup(?)
  512. ;
  513. codeloc        db    execlen dup(?)    ; code
  514. ;    
  515. div0_off    dw    ?        ; divide by zero vector save
  516. div0_seg    dw    ?
  517. xfcb1        db    16 dup(?)    ; default FCB
  518. xfcb2        db    16 dup(?)    ; default FCB
  519. filename    db    82 dup(?)    ; exec filename
  520. progpars    db    128 dup(?)    ; command tail
  521.         db    stacklen dup(?)    ; stack space
  522. ;
  523. parseg2    ends
  524. ;
  525. mystack        equ    progpars+128+stacklen
  526. parend        equ    mystack
  527. ;
  528. reslen    = (parend - parbeg)
  529. ;
  530. keep_paras    = (reslen + 15) shr 4    ; paragraphs to keep
  531. swapbeg        = keep_paras shl 4    ; start of swap space
  532. savespace    = swapbeg - 5ch        ; length of overwritten area
  533. ;
  534. ;    Space for saving the part of the memory image below the
  535. ;    swap area that is overwritten by our code.
  536. ;
  537.     .data
  538. save_dat    db    savespace dup(?)
  539. ;       
  540.     .code
  541. ;
  542. emm_name    db    'EMMXXXX0'
  543. ;
  544.     IFDEF    PASCAL
  545.     IFDEF    FARCALL
  546. do_spawn    PROC    far pmethod: byte, swapfname: dword, execfname: dword, params: dword, envlen: word, envp: dword
  547.     ELSE
  548. do_spawn    PROC    near pmethod: byte, swapfname: dword, execfname: dword, params: dword, envlen: word, envp: dword
  549.     ENDIF
  550.     ELSE
  551. do_spawn    PROC    uses si di,pmethod:byte,swapfname:ptr byte,execfname:ptr byte,params:ptr byte,envlen:word,envp:ptr byte
  552.     ENDIF
  553. ;
  554.     public    do_spawn
  555. ;
  556.     push    ds
  557.     mov    ax,ds
  558.     mov    es,ax
  559.     cld
  560. ;
  561.     IFDEF    PASCAL
  562.     mov    ax,prefixseg
  563.     ELSE
  564.     mov    ax,_psp
  565.     ENDIF
  566.     mov    ds,ax            ; DS points to PSP
  567. ;
  568. ;    Check if spawn is too low in memory
  569. ;
  570.     mov    bx,cs
  571.     mov    dx,offset doexec
  572.     mov    cl,4
  573.     shr    dx,cl
  574.     add    bx,dx            ; normalized start of this code
  575.     mov    dx,keep_paras        ; the end of the modified area
  576.     add    dx,ax            ; plus PSP = end paragraph
  577.     cmp    bx,dx
  578.     ja    save_mem    ; ok if start of code > end of low mem
  579.     pop    ds
  580.     mov    ax,500h
  581.     ret
  582. ;
  583. ;    Save the memory below the swap space
  584. ;
  585. save_mem:
  586.     mov    si,5ch
  587.     mov    di,offset save_dat
  588.     mov    cx,savespace
  589.     rep movsb
  590. ;
  591.     mov    es,ax            ; ES points to PSP
  592.     dec    ax
  593.     mov    ds,ax            ; DS now points to MEM CTL
  594.     mov    bx,word ptr ds:3    ; allocated paragraphs
  595.     mov    es:memsize,bx
  596. ;
  597. ;    Now walk the chain of memory blocks, chaining all blocks
  598. ;    belonging to this process. Four unused words in the MCB are
  599. ;    used to store the paragraph address and size of the next block.
  600. ;
  601.     push    es
  602.     mov    bx,ds:owner        ; the current process
  603.     mov    di,next_mcb
  604. ;
  605. mcb_chain:
  606.     cmp    ds:id,4dh        ; normal block?
  607.     jne    mcb_ready        ; ready if end
  608.     mov    cx,ds
  609.     add    cx,ds:paras        ; start + length
  610.     inc    cx            ; next MCB
  611.     mov    ds,cx
  612.     cmp    bx,ds:owner        ; our process?
  613.     jne    mcb_chain        ; loop if not
  614.     mov    es:[di].next_addr,ds
  615.     mov    ax,ds:paras
  616.     mov    es:[di].next_size,ax
  617.     inc    ax
  618.     push    ax
  619.     mov    cl,blockshift
  620.     shr    ax,cl            ; number of blocks
  621.     mov    es:[di].next_blocks,ax
  622.     pop    ax
  623.     and    ax,blockmask
  624.     mov    cl,4
  625.     shl    ax,cl            ; convert to no. of bytes
  626.     mov    es:[di].next_rest,ax
  627.     mov    ax,ds
  628.     mov    es,ax
  629.     mov    di,mcb_reserved
  630.     jmp    mcb_chain
  631. ;
  632. mcb_ready:
  633.     mov    es:[di].next_addr,0
  634.     pop    es
  635. ;
  636.     mov    es:page_frame,0
  637. ;
  638. ;    Swap out memory
  639. ;
  640.     mov    al,pmethod
  641.     mov    es:method,al
  642.     test    al,SWAPPING
  643.     jnz    do_swap
  644.     jmp    no_swap
  645. ;
  646. do_swap:
  647.     mov    bx,es:memsize
  648.     sub    bx,keep_paras        ; minus resident paragraphs
  649.     mov    ax,bx
  650.     mov    cl,blockshift
  651.     shr    ax,cl            ; number of blocks in swapfile
  652.     mov    es:rd_blocks,ax
  653.     xchg    ax,bx
  654.     and    ax,blockmask
  655.     mov    cl,4
  656.     shl    ax,cl            ; number of bytes in last block
  657.     mov    es:rd_rest,ax
  658. ;
  659. ;    Check for EMS swap
  660. ;
  661.     test    pmethod,USE_EMS
  662.     jnz    try_ems
  663.     jmp    no_ems
  664. ;
  665. try_ems:
  666.     or    ax,ax
  667.     jz    no_rest
  668.     inc    bx            ; number of EMS blocks needed
  669. ;
  670. ;    For EMS, we have to determine the total number of blocks
  671. ;    for all memory blocks.
  672. ;
  673. no_rest:
  674.     mov    si,next_mcb
  675.     push    es
  676.     pop    ds
  677. ;
  678. tot_blocks:
  679.     mov    cx,[si].next_addr
  680.     or    cx,cx
  681.     jz    tot_ready
  682.     add    bx,[si].next_blocks
  683.     mov    ax,[si].next_rest
  684.     mov    ds,cx
  685.     mov    si,mcb_reserved
  686.     or    ax,ax
  687.     jz    tot_blocks
  688.     inc    bx
  689.     jmp    tot_blocks
  690. ;
  691. tot_ready:
  692.     push    bx
  693.     push    es
  694.     mov    al,EMM_INT
  695.     mov    ah,35h
  696.     int    21h            ; get EMM int vector
  697.     mov    ax,cs
  698.     mov    ds,ax
  699.     mov    si,offset emm_name
  700.     mov    di,10
  701.     mov    cx,8
  702.     repz cmpsb
  703.     pop    es
  704.     pop    bx
  705.     jz    ems_ok_1
  706.     jmp    no_ems
  707. ;
  708. ems_ok_1:
  709.     mov    ah,40h
  710.     int    EMM_INT
  711.     or    ah,ah
  712.     jz    ems_ok_2
  713.     jmp    no_ems
  714. ;
  715. ems_ok_2:
  716.     mov    ah,46h
  717.     int    EMM_INT
  718.     or    ah,ah
  719.     jz    ems_ok_3
  720.     jmp    no_ems
  721. ;
  722. ems_ok_3:
  723.     cmp    al,30h
  724.     jae    ems_ok_4
  725.     jmp    no_ems
  726. ;
  727. ems_ok_4:
  728.     push    bx
  729.     mov    ah,41h
  730.     int    EMM_INT
  731.     mov    es:page_frame,bx
  732.     pop    bx
  733.     or    ah,ah
  734.     jz    ems_ok_5
  735.     jmp    no_ems
  736. ;
  737. ;    EMS present, try to allocate pages
  738. ;
  739. ems_ok_5:
  740.     mov    ah,43h
  741.     int    EMM_INT
  742.     or    ah,ah
  743.     jz    ems_ok_6
  744.     jmp    no_ems
  745. ;
  746. ;    EMS pages allocated, swap to EMS
  747. ;
  748. ems_ok_6:
  749.     mov    es:handle,dx
  750.     push    es
  751.     mov    ax,es
  752.     mov    ds,ax
  753.     push    ds
  754.     mov    ax,next_mcb
  755.     push    ax
  756.     push    es:rd_rest
  757.     mov    cx,es:rd_blocks
  758.     mov    es,es:page_frame
  759.     xor    bx,bx
  760.     mov    si,swapbeg
  761. ;
  762. swap_ems:
  763.     jcxz    swapout_erest        ; jump if no full blocks
  764. ;
  765. swapout_ems:
  766.     push    cx
  767.     mov    ax,4400h            ; map page, phys = 0
  768.     int    EMM_INT
  769.     or    ah,ah
  770.     jnz    ems_error
  771.     mov    cx,blocksize
  772.     mov    di,0
  773.     push    si
  774.     rep movsb
  775.     pop    si
  776.     mov    ax,ds
  777.     add    ax,blocksize_paras
  778.     mov    ds,ax
  779.     pop    cx
  780.     inc    bx
  781.     loop    swapout_ems
  782. ;
  783. swapout_erest:
  784.     pop    cx            ; remaining bytes
  785.     push    cx
  786.     push    cx
  787.     jcxz    swapout_erdy
  788.     mov    ax,4400h        ; map page, phys = 0
  789.     int    EMM_INT
  790.     or    ah,ah
  791.     jnz    ems_error
  792.     mov    di,0
  793.     rep movsb
  794.     inc    bx
  795. ;
  796. swapout_erdy:
  797.     add    sp,4
  798.     pop    si            ; next offset
  799.     pop    ds            ; segment
  800. ;
  801.     mov    ax,[si].next_addr
  802.     or    ax,ax
  803.     jz    ems_complete
  804.     push    ax
  805.     mov    cx,mcb_reserved
  806.     push    cx
  807.     mov    cx,[si].next_blocks
  808.     mov    si,[si].next_rest
  809.     push    si
  810.     mov    si,0
  811.     mov    ds,ax
  812.     jmp    swap_ems
  813. ;
  814. ems_complete:
  815.     pop    es
  816.     jmp    no_swap
  817. ;
  818. ems_error:
  819.     add    sp,8
  820.     pop    es
  821.     mov    ah,45h            ; release EMS pages on error
  822.     int    EMM_INT
  823. ;
  824. ;    No or not enough EMS storage, swap to file
  825. ;
  826. no_ems:
  827.     mov    es:page_frame,0
  828.     IF    ptrsize
  829.     lds    dx,swapfname
  830.     ELSE
  831.     pop    ds
  832.     push    ds
  833.     mov    dx,swapfname
  834.     ENDIF
  835.     inc    dx
  836.     mov    cx,2            ; hidden
  837.     mov    ah,3ch            ; create file
  838.     test    pmethod,CREAT_TEMP
  839.     jz    no_temp
  840.     mov    ah,5ah
  841. ;
  842. no_temp:
  843.     int    21h
  844.     jc    spawn_error
  845.     mov    es:handle,ax
  846.     mov    bx,ax
  847. ;
  848. ;    save the file name in the base area
  849. ;
  850.     mov    di,tempfilename
  851.     mov    cx,81
  852.     mov    si,dx
  853.     rep movsb
  854. ;
  855.     mov    ax,es
  856.     mov    ds,ax
  857.     push    ds
  858.     mov    si,next_mcb
  859.     mov    cx,es:rd_blocks
  860.     mov    di,es:rd_rest
  861.     mov    dx,swapbeg
  862. ;
  863. swap_file:
  864.     jcxz    swout_rest
  865. ;
  866. swout_blocks:
  867.     push    cx
  868.     mov    cx,blocksize
  869.     mov    ah,40h
  870.     int    21h
  871.     pop    cx
  872.     jc    spawn_error
  873.     mov    ax,ds
  874.     add    ax,blocksize_paras
  875.     mov    ds,ax
  876.     loop    swout_blocks
  877. ;
  878. swout_rest:
  879.     mov    cx,di
  880.     jcxz    swap_ready
  881.     mov    ah,40h
  882.     int    21h
  883.     jnc    swap_ready
  884. ;
  885. spawn_error:
  886.     add    sp,2
  887.     pop    ds
  888.     mov    ax,100h
  889.     ret
  890. ;
  891. swap_ready:
  892.     pop    ds
  893.     mov    ax,[si].next_addr
  894.     or    ax,ax
  895.     jz    swap_complete
  896.     mov    cx,[si].next_blocks
  897.     mov    di,[si].next_rest
  898.     mov    dx,0
  899.     mov    si,mcb_reserved
  900.     mov    ds,ax
  901.     push    ds
  902.     jmp    swap_file
  903. ;
  904. ;    Swapout complete
  905. ;
  906. swap_complete:
  907.     mov    ah,3eh
  908.     int    21h            ; close file
  909. ;
  910. no_swap:
  911. ;
  912. ;    Prepare exec parameter block
  913. ;
  914.     mov    ax,es
  915.     mov    es:expar.fcb1seg,ax
  916.     mov    es:expar.fcb2seg,ax
  917.     mov    es:expar.pparseg,ax
  918.     mov    es:expar.envseg,0
  919. ;
  920. ;    The 'zero' word is located at 80h in the PSP, the start of
  921. ;    the command line. So as not to confuse MCB walking programs,
  922. ;    a command line length of zero is inserted here.
  923. ;
  924.     mov    es:zero,0d00h        ; 00h,0dh = empty command line
  925. ;
  926. ;    Init default fcb's by parsing parameter string
  927. ;
  928.     IF    ptrsize
  929.     lds    si,params
  930.     ELSE
  931.     pop    ds
  932.     push    ds
  933.     mov    si,params
  934.     ENDIF
  935.     push    si
  936.     mov    di,xfcb1
  937.     mov    es:expar.fcb1,di
  938.     push    di
  939.     mov    cx,16
  940.     xor    ax,ax
  941.     rep stosw            ; init both fcb's to 0
  942.     pop    di
  943.     mov    ax,2901h
  944.     IFDEF    PASCAL
  945.     inc    si
  946.     ENDIF
  947.     int    21h
  948.     mov    di,xfcb2
  949.     mov    es:expar.fcb2,di
  950.     mov    ax,2901h
  951.     int    21h
  952.     pop    si
  953. ;
  954. ;    move command tail string into low core
  955. ;
  956.     mov    cl,byte ptr [si]
  957.     xor    ch,ch
  958.     mov    di,progpars
  959.     mov    es:expar.ppar,di
  960.     inc    cx
  961.     rep movsb
  962.     mov    al,0dh
  963.     stosb
  964. ;
  965. ;    move filename string into low core
  966. ;
  967.     IF    ptrsize
  968.     lds    si,execfname
  969.     ELSE
  970.     mov    si,execfname
  971.     ENDIF
  972.     lodsb
  973.     mov    cl,al
  974.     xor    ch,ch
  975.     mov    di,filename
  976.     rep movsb
  977.     xor    al,al
  978.     stosb
  979. ;
  980. ;    Setup environment copy
  981. ;
  982.     mov    bx,keep_paras        ; paras to keep
  983.     mov    cx,envlen        ; environment size
  984.     jcxz    no_environ        ; go jump if no environment
  985.     mov    ax,cx            ; convert envsize to paras
  986.     add    ax,15
  987.     shr    ax,1
  988.     shr    ax,1
  989.     shr    ax,1
  990.     shr    ax,1
  991.     add    bx,ax            ; add envsize to paras to keep
  992.     IF    ptrsize
  993.     lds    si,envp
  994.     ELSE
  995.     mov    si,envp
  996.     ENDIF
  997. ;
  998.     mov    ax,es            ; low core segment
  999.     add    ax,keep_paras        ; plus fixed paras
  1000.     mov    es:expar.envseg,ax    ; = new environment segment
  1001. ;
  1002. ;    Save stack regs, switch to local stack
  1003. ;
  1004. no_environ:
  1005.     mov    es:save_ss,ss
  1006.     mov    es:save_sp,sp
  1007.     mov    ax,es
  1008.     mov    ss,ax
  1009.     mov    sp,mystack
  1010. ;
  1011.     push    cx
  1012.     push    si
  1013.     push    ds
  1014. ;
  1015. ;    Move code into low core
  1016. ;
  1017.     mov    di,codeloc
  1018.     call    near ptr nextloc        ; where are we ?
  1019. nextloc:
  1020.     pop    si            ; si = abs. addr of 'nextloc'
  1021.     push    si
  1022.         add     si,doexec-nextloc
  1023.         mov     cx,execlen
  1024.     push    cs
  1025.     pop    ds
  1026.         rep movsb                       ; move down code
  1027. ;
  1028. ;    save and patch INT0 (division by zero) vector
  1029. ;
  1030.     xor    ax,ax
  1031.     mov    ds,ax
  1032.     mov    ax,word ptr ds:0
  1033.     mov    es:div0_off,ax
  1034.     mov    ax,word ptr ds:2
  1035.     mov    es:div0_seg,ax
  1036.     mov    word ptr ds:0,codeloc + iretoff
  1037.     mov    word ptr ds:2,es
  1038. ;
  1039. ;    Push return address on local stack
  1040. ;
  1041.     pop    ax
  1042.     add    ax,exec_cont-nextloc
  1043. ;
  1044.     pop    ds            ; pop environment segment
  1045.     pop    si            ; pop environment offset
  1046.     pop    cx            ; pop environment length
  1047.     mov    di,swapbeg        ; pop environment destination
  1048. ;
  1049.     push    cs            ; push return segment
  1050.     push    ax            ; push return offset
  1051.     mov    es:spx,sp        ; save stack pointer
  1052. ;
  1053. ;    Goto low core code
  1054. ;
  1055.     push    es            ; push entry segment
  1056.         mov    ax,codeloc
  1057.         push    ax            ; push entry offset
  1058. ;    ret    far            ; can't use RET here because
  1059.     db    0cbh            ; of .model
  1060. ;
  1061. ;----------------------------------------------------------------
  1062. ;
  1063. ;    low core code will return to this location
  1064. ;
  1065. exec_cont:
  1066.     mov    ss,es:save_ss        ; reload stack
  1067.     mov    sp,es:save_sp
  1068. ;
  1069. ;    restore INT0 (division by zero) vector
  1070. ;
  1071.     xor    cx,cx
  1072.     mov    ds,cx
  1073.     mov    cx,es:div0_off
  1074.     mov    word ptr ds:0,cx
  1075.     mov    cx,es:div0_seg
  1076.     mov    word ptr ds:2,cx
  1077. ;
  1078.     pop    ds
  1079. ;
  1080.     mov    bx,es:page_frame    ; save EMS status in BX
  1081. ;
  1082. ;    Restore overwritten part of program
  1083. ;
  1084.     mov    si,offset save_dat
  1085.     mov    di,5ch
  1086.     mov    cx,savespace
  1087.     rep movsb
  1088. ;
  1089.     or    bx,bx            ; EMS swap?
  1090.     jnz    exec_finish        ; ready if yes
  1091. ;
  1092.     push    ds            ; else delete swapfile
  1093.     push    ax
  1094.     IF    ptrsize
  1095.     lds    dx,swapfname
  1096.     ELSE
  1097.     mov    dx,swapfname
  1098.     ENDIF
  1099.     inc    dx
  1100. ;
  1101.     mov    ah,41h
  1102.     int    21h            ; delete file
  1103.     pop    ax
  1104.     pop    ds
  1105. ;
  1106. exec_finish:
  1107.     ret
  1108. ;    
  1109. do_spawn    ENDP
  1110. ;
  1111.         END
  1112.  
  1113.  
  1114.